package server

import (
	"fmt"
	grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/reflection"
	"google.golang.org/grpc/status"
	"longmen/server/config/global"
	"net"
	"runtime"
)

type ServerOption func(o *Server)

func Network(n string) ServerOption {
	return func(s *Server) {
		s.network = n
	}
}

func Address(a string) ServerOption {
	return func(s *Server) {
		s.address = a
	}
}

func Listener(ln net.Listener) ServerOption {
	return func(s *Server) {
		s.ln = ln
	}
}

func UnaryServerInterceptor(unary ...grpc.UnaryServerInterceptor) ServerOption {
	return func(s *Server) {
		s.unary = unary
	}
}

func RecoveryOption() []grpc_recovery.Option {
	customFunc := func(p interface{}) (err error) {
		stack := make([]byte, 4<<10)
		length := runtime.Stack(stack, true)
		stack = stack[:length]
		msg := fmt.Sprintf("[PANIC RECOVER] %v %s\n", err, stack[:length])
		global.Log.Desugar().Error(msg)
		return status.Errorf(codes.Unknown, "panic triggered: %v", p)
	}
	// Shared options for the logger, with a custom gRPC code to log level function.
	return []grpc_recovery.Option{
		grpc_recovery.WithRecoveryHandler(customFunc),
	}
}

func StreamInterceptor(stream ...grpc.StreamServerInterceptor) ServerOption {
	return func(s *Server) {
		s.stream = stream
	}
}

type Server struct {
	*grpc.Server
	ln      net.Listener
	network string
	address string
	unary   []grpc.UnaryServerInterceptor
	stream  []grpc.StreamServerInterceptor
}

func New(opts ...ServerOption) *Server {
	srv := &Server{}
	for _, o := range opts {
		o(srv)
	}
	opt := []grpc.ServerOption{
		grpc.UnaryInterceptor(
			grpcmiddleware.ChainUnaryServer(
				srv.unary...,
			),
		),
	}
	srv.Server = grpc.NewServer(opt...)
	reflection.Register(srv.Server)
	return srv
}

func (s *Server) GetLn() net.Listener {
	return s.ln
}

func (s *Server) GetAddress() string {
	return s.address
}

func (s *Server) Start() error {
	if err := s.Listener(); err != nil {
		return err
	}
	return s.Serve(s.ln)
}

func (s *Server) Stop() error {
	// TODO 停止服务操作
	return nil
}

func (s *Server) Listener() error {
	if s.ln == nil {
		ln, err := net.Listen(s.network, s.address)
		if err != nil {
			return err
		}
		s.ln = ln
	}
	return nil
}
